iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0

昨天我們替 Memora 加入第一個 Tool:

count_english_words

模型可以判斷是否需要精確計算英文單字數量,再由 Application 執行 Python Function,最後把 Tool Result 送回模型。

現在很容易出現一個問題:

Memora 已經會使用 Tool,所以它現在就是 Agent 了嗎?

如果只看功能,可能會想直接回答「是」。但從程式控制流程來看,目前更精確的說法是:

Day 25 的 Memora 是一個具備 Memory 的 Tool-using Chatbot,而且 Tool 執行被包在固定 Workflow 裡;它已經有 Agent 的部分元素,但還沒有 Agent Loop。

今天不另外建立一套程式,也不提前完成 Day 27。這一篇會直接拆開昨天的 run_model_with_tools(),確認現在到底是誰在決定下一步。


一、先建立這個系列使用的 Agent 定義

「Agent」在不同文章、產品或 Framework 裡,範圍可能不完全相同。如果只用名稱判斷,很容易變成:

有 System Prompt
→ Agent

有 Memory
→ Agent

有 Tool
→ Agent

這些說法都太寬。

為了讓後面的程式可以驗證,這個系列採用一個操作型定義:

Agent 是一個能根據目前目標與 Observation,在受限制的執行環境中反覆選擇下一個 Action,直到符合 Stop Condition 的系統。

核心不是名稱,也不是模型是否看起來很聰明,而是執行期間是否真的存在:

Goal
→ Decide
→ Action
→ Observation
→ Decide Again
→ Stop

OpenAI 的 Agent 文件也把 Agent Loop 描述為:呼叫模型、檢查輸出、執行 Tool Call、繼續執行,直到模型產生不再需要 Tool 的最終答案。


二、Chatbot、Tool-using Chatbot、Workflow 與 Agent

先用同一組標準比較:

類型 誰決定主要步驟 能否使用 Tool 能否根據結果再次決策 如何停止
Chatbot Application 呼叫一次模型 不一定 通常不能 模型回傳回答
Tool-using Chatbot 模型可選擇 Tool,但流程通常有限 可以 視實作而定 多半由固定流程結束
Workflow Python 或 Graph 預先決定路徑 可以 只走預先寫好的 Branch 程式規則
Agent 模型依 Goal 與 Observation 選擇下一步 通常可以 可以反覆決策 Final Answer、限制或中止條件

這四個名稱不是互斥的產品分類。一個實際系統可以同時是:

對外看起來是 Chatbot
內部使用固定 Workflow
其中某一段包含 Agent Loop

真正需要分辨的是每一段流程的 Control Flow 由誰掌握。


三、Memory 和 Agent 是兩個不同維度

Memora 在 Day 25 以前已經有:

Conversation History
Summary
User Profile
Long-term Memory
Semantic Retrieval
Memory Policy
Importance、Recency 與 Reconciliation

但這些能力不會自動把 Chatbot 變成 Agent。

Memory 回答的是:

系統能保留什麼狀態?
這一輪應該取回哪些過去資訊?

Agent Control Flow 回答的是:

為了完成目前目標,下一個動作是什麼?
看到執行結果後,是否還需要其他動作?
什麼時候應該停止?

因此可能存在:

有 Long-term Memory、但沒有 Agent Loop 的 Chatbot

沒有 Long-term Memory、但能完成多步任務的 Agent

同時具有 Memory 與 Agent Loop 的 Agent

這個系列前 24 天先建立 Memory,是為了讓 Day 28 的 Agent 可以操作一套已經清楚分層的 Memory System,而不是因為「有 Memory」就已經等於 Agent。


四、直接檢查 Day 25 的 Control Flow

昨天的 run_model_with_tools() 可以先縮成以下結構:

response = client.responses.create(
    tools=TOOLS,
    tool_choice="auto",
    parallel_tool_calls=False,
    # 其他參數保留
)

function_calls = [
    item
    for item in response.output
    if item.type == "function_call"
]

if not function_calls:
    return response.output_text

tool_call = function_calls[0]
tool_output = execute_tool_call(
    tool_call
)

final_response = client.responses.create(
    tools=TOOLS,
    tool_choice="none",
    parallel_tool_calls=False,
    # response.output 與 tool_output
    # 已加入新的 Input
)

return final_response.output_text

第一個 Request 中:

tool_choice="auto"

所以模型可以選擇直接回答,或要求一次 Tool Call。這部分已經具有有限的決策能力。

但是第二個 Request 使用:

tool_choice="none"

表示模型看完 Tool Result 後,只能整理 Final Response,不能再選擇另一個 Action。

因此整條路徑已經被 Application 寫死:

最多一次 Tool Call
→ 一定進入 Final Response
→ Return

這是一個有條件分支的 Workflow,還不是可以反覆決策的 Agent Loop。


五、Day 25 已經有 Action 與 Observation

雖然還不是完整 Agent Loop,昨天其實已經完成兩個重要元件。

Action

模型回傳:

function_call
name = count_english_words
arguments = {...}

這代表模型選擇了一個可執行動作。

Observation

Application 執行 Function 後回傳:

function_call_output
call_id = ...
output = {...}

這是 Action 的執行結果,也就是下一次模型判斷可以觀察到的資料。

目前真正缺少的是:

Observation
→ 再次決定 Action 或 Final Answer

Day 25 的模型雖然看得到 Observation,但 tool_choice="none" 已經替它決定:下一步只能回答。


六、一個 Tool Call 不一定足以完成任務

假設使用者要求:

請比較下面兩句話的英文單字數量,告訴我哪一句比較長:

A: I study English every day.
B: I have studied English for three years.

目前的 Tool 一次只接受:

{
    "text": "一段英文"
}

合理的處理步驟可能是:

Action 1
count_english_words(A)

Observation 1
A = 5

Action 2
count_english_words(B)

Observation 2
B = 7

Final Answer
B 比 A 多 2 個單字

Day 25 執行完 Action 1 後就強制進入 Final Response,因此模型不能再呼叫同一個 Tool 計算B。

這個例子不需要新增第二個 Tool,就能看出:

Agent Loop 的關鍵不是 Tool 數量,而是模型能否根據 Observation 繼續選擇下一個步驟。


七、固定 Workflow 和 Agent Loop 的差別

假設我們直接在 Python 寫:

count_a = count_english_words(
    sentence_a
)

count_b = count_english_words(
    sentence_b
)

result = compare_counts(
    count_a,
    count_b
)

這可以正確完成任務,但執行順序完全由程式預先決定,因此它是 Workflow。

Agent Loop 則會讓模型在每一步判斷:

目前已有什麼資料?
還缺少什麼?
下一個 Tool Call 是什麼?
資訊是否已經足以回答?

兩者沒有誰一定比較好。

當步驟明確、規則穩定時,Workflow 通常更容易測試、成本更低,也更可預測。只有當任務路徑會依中間結果變化時,才需要增加 Agentic Control。因此「能寫成 Agent」不是採用 Agent 的充分理由。


八、Agent 需要哪些最小元件?

在這個系列裡,一個最小 Agent Run 至少包含:

元件 在 Memora 中的對應內容
Goal Current User Message
Instructions SYSTEM_PROMPT
State input_messages 與累積的 Output Items
Actions TOOLS 中允許的 Function
Executor execute_tool_call()
Observation function_call_output
Decision Maker LLM
Loop 重複 Model → Tool → Model
Stop Condition Final Answer、Step Limit 或錯誤中止

Day 25 已經有其中大部分元件,卻缺少通用的 Loop 與 Stop Control。

這也是為什麼明天不需要推翻整個程式。Day 27 的主要修改會集中在:

run_model_with_tools()

其餘 Memory、Tool Definition 與 Dispatcher 都可以繼續沿用。


九、Agent 的自主性仍然受到 Application 限制

Agent 並不是讓模型取得所有權限,再自行處理一切。Application 仍然要決定:

提供哪些 Tools
每個 Tool 可以接受哪些 Arguments
哪些動作需要人工確認
一次 Run 最多可以執行幾步
Tool Output 可以有多大
發生錯誤時如何處理
哪些資料不能送給模型

模型的自主性只存在於這些邊界之內:

Application 定義 Action Space
LLM 在 Action Space 中選擇下一步

Day 25 的 TOOL_HANDLERS Allowlist、Argument Validation 與長度限制都要保留。加入 Agent Loop 之後,它們反而更重要,因為同一個 Run 可能執行多次 Tool Call。


十、Stop Condition 不是一句「完成就停止」

如果只有:

while True:

卻沒有明確停止條件,Agent 可能反覆呼叫 Tool,持續消耗 Token,甚至重複執行具有副作用的動作。

Day 27 至少要處理三種停止結果:

正常停止
模型沒有再要求 Tool,已產生 Final Answer

限制停止
達到 MAX_AGENT_STEPS

錯誤停止
API、Arguments 或 Tool Execution 發生無法繼續的錯誤

未來如果 Tool 會修改資料,還可能需要:

Approval Pause

也就是先請使用者確認,再執行具有風險或不可逆的 Action。

今天先定義這些邊界,不在 Day 26 偷跑實作 Loop。


十一、Agent 不需要把內部思考全部顯示出來

有時候會把 Agent 流程寫成:

Thought
→ Action
→ Observation

但工程上真正需要保存與檢查的是:

模型輸出的 Tool Call
Application 實際執行的 Action
Tool 回傳的 Observation
最後的 Result 或 Stop Reason

不需要要求模型輸出隱藏的逐步推理,也不應把一段看起來像思考過程的文字當成可靠的控制訊號。

Memora 會繼續使用 Responses API 的結構化 Output Items 判斷:

function_call
function_call_output
output_text

控制流程依據的是可驗證資料,不是自由格式的「我接下來想做……」。


十二、Conversation State 的管理方式也不要突然改掉

Day 25 使用 Application-managed State:

next_input = list(input_messages)
next_input += response.output
next_input.append(
    function_call_output
)

Day 27 會繼續使用同一種策略,把每一輪新的:

response.output
function_call_output

加入同一個 Working Input。

不會在中途突然改用:

previous_response_id

因為同時重播完整 Local Input,又使用 Server-managed Continuation,可能讓相同 Context 被重複帶入。

OpenAI 的 Agent 文件也建議一段 Conversation 選擇一種主要 State Strategy。Memora 目前重視可觀察與可控制,所以繼續由 Application 管理這次 Agent Run 的 State。


十三、Day 25 的 Memora 應該怎麼分類?

現在逐項檢查:

能力 Day 25 是否具備
多輪 Conversation State 有
Long-term Memory 有
Memory Retrieval 有
Tool Definition 有
模型選擇是否使用 Tool 有
Application 執行 Tool 有
Tool Result 回到模型 有
根據 Observation 再選 Action 沒有
通用 Agent Loop 沒有
Step Limit 沒有
動態 Stop 判斷 沒有

所以目前最精確的定位是:

一個有狀態、有長期記憶,並能在固定流程中使用一次 Tool 的 Tool-using Chatbot。

它距離 Agent 已經不遠,但這個差距不是再加一個更長的 Prompt,而是補上 Runtime Control Loop。


十四、Day 27 會保留什麼、修改什麼?

明天會完整保留:

count_english_words()
TOOLS
TOOL_HANDLERS
execute_tool_call()
SYSTEM_PROMPT
Short-term Memory
User Profile
Long-term Memory Retrieval
Day 24 的 Memory Write Path

主要只修改 Day 25 的:

run_model_with_tools()

目前是:

第一次 Model Call
→ 最多一次 Tool Call
→ 強制 Final Response

Day 27 會變成:

在 Step Limit 內重複:

Model Call
→ 有 Tool Call:執行並加入 Observation
→ 沒有 Tool Call:回傳 Final Answer

這樣才是從昨天的程式自然演進,而不是突然改用另一套 Agent Framework。


Day 26 小結

今天沒有增加新的 Tool,也沒有改動 Day 24 的 Memory System。這是刻意保留的一個 Architecture Checkpoint:先確認目前的控制流程,再開始增加自主性。

四個概念可以簡化成:

Chatbot
→ 主要目標是產生回答

Tool-using Chatbot
→ 可以要求 Application 執行 Function

Workflow
→ 程式預先決定執行路徑

Agent
→ 模型能根據 Observation 反覆選擇下一個 Action,
  直到符合 Stop Condition

昨天的 Memora 已經有:

Goal
Action
Executor
Observation

但仍然缺少:

Observation 後再次 Decision
可重複的 Loop
明確的 Step Limit
通用 Stop Condition

今天最重要的觀念是:

會使用 Tool 不等於已經是 Agent。真正的分界在於:模型能不能根據執行結果持續決定下一步,而不是只走完 Application 預先寫好的單次路徑。

Day 27|打造 Agent Loop:Action、Observation 與 Stop

下一篇會直接修改 Day 25 的 run_model_with_tools():移除第二次 Request 的強制 tool_choice="none",加入有限次數的 Loop,讓 Memora 可以反覆執行:

Decision
→ Action
→ Observation
→ Decision

同時實作 MAX_AGENT_STEPS、Final Answer 判斷、Token 累積與錯誤停止,完成第一個可控的 Agent Loop。


參考資料


上一篇
Day 25|Tool Calling 是什麼?讓 LLM 不只會回答
下一篇
Day 27|打造 Agent Loop:Action、Observation 與 Stop
系列文
從 Stateless LLM 到 Agentic Memory:30 天打造會記憶的 AI Agent 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言